#ifndef OBJECTPOOL_H
#define OBJECTPOOL_H
#include "MemoryBlock.h" // Base class: stomped_reality::memory::MemoryBlock
#include "StompedReality/Core.h"
#include "StompedReality/Core/Component.h"
#include "StompedReality/Core/Delegate.h"
#include "StompedReality/Core/TypeSystem/TypeSystem.h"
#include <assert.h>
#include <functional>
namespace StompedReality
{
namespace Core
{
/*!
 * \author	Stefan Kazalski
 *
 * \brief	Object storage
 *
 *    Stores objectss of type t into a char*.<br>
 *    Provides getter and setter for objects.
 */
template <class T>
class DLL_EXPORT ObjectPool : public StompedReality::Core::MemoryBlock<T>
{
 private:

  static const ID INDEX_MASK = 0xffff;
  static const ID NEW_OBJECT_ID_ADD = 0x10000;
  static const ID USHORT_MASK = 0xffff;
  struct INDEX
  {
    ID id;
    unsigned short index;
    unsigned short Next;
    INDEX ( int idx )
    {
      this->id = idx;
      this->Next = idx + 1;
      this->index = USHORT_MASK;
    }

  };

 protected:

  StompedReality::type_system::TypeInfo* m_TypeInfo;
  int m_ObjectSize;
  unsigned m_NumOfObjects;
  unsigned m_Capacity;
  unsigned short m_FreelistEnqueue;
  unsigned short m_FreelistDequeue;
  char* m_Indices;
  char* m_Data;
  inline INDEX* GetIndex ( int i )
  {
    return ( INDEX* ) ( this->m_Indices + i * sizeof ( INDEX ) );
  }

  inline T* GetDataObject ( int i )
  {
    return ( T* ) ( this->m_Data + i * this->m_ObjectSize );
  }

  void Initialize ( int capacity );

 public:

  //! \brief	Constructor.
  //! \param	capacity	Number of objects.
  ObjectPool ( int capacity );

  //! \brief	Constructor.
  //! \param	capacity	Number of objects.
  ObjectPool ( int capacity, StompedReality::type_system::TypeInfo* info );

  //! \brief	Destructor.
  virtual ~ObjectPool( );

  //! \brief	GetCapacity
  int GetCapacity( )
  {
    return this->m_Capacity;
  }

  //! \brief	Checks if an object is stored at the index.
  //! \param	idx	The index.
  //! \return	Does obejct exists at this position.
  bool IsObjectAt( int idx )
  {
    INDEX* in = this->GetIndex ( idx );
    return in->index != USHORT_MASK;
  }

  //! \brief Return object at index.
  //! \param	idx	The index.
  //! \return	Return the object or NULL if it does not exits.
  T* GetObjectAt( int idx ) {
    INDEX* in = this->GetIndex ( idx );
    if (in->index == USHORT_MASK)
      return 0;
    return this->GetDataObject( in->index );
  }

  //! \brief TypeId getter.
  StompedReality::TYPE_ID GetTypeID( );

  //! \brief	Creates new object.
  //!			Creates a new object and returns its id.
  //!			Calls objects onCreation() method.
  //! \return	The object id.ID createObject();
  ID Create ( StompedReality::IGame* game );

  //! \brief	Tests if object with id exists.
  bool Contains ( ID id );

  //! \brief	Returns the object.
  //! \return	Object if exists otherwrise NULL.
  T* Get ( ID id );

  //! \brief Removes an object from the block.
  void Remove ( ID id );
  static MemoryBlock<T>* CreateObjectPool ( int capacity )
  {
    if ( !StompedReality::type_system::TypeSystem::HasType<T>( ) )
      return 0;
    else
      return new ObjectPool<T> ( capacity );
  }

  static MemoryBlock<T>* CreateObjectPool ( int capacity, StompedReality::type_system::TypeInfo* info )
  {
    return new ObjectPool<T> ( capacity, info );
  }

  template <typename RETURN_TYPE>
  std::function<RETURN_TYPE ( )> CreateGetterDelegate( StompedReality::ID id, const char* name )
  {
    return StompedReality::DelegateValueGetter<RETURN_TYPE>((char*)this->Get( id ),
                                                            this->m_TypeInfo->findField( name ).offset );
  }

  template <typename TYPE>
  std::function<void ( TYPE )> CreateSetterDelegate( StompedReality::ID id, const char* name )
  {
    return StompedReality::DelegateValueSetter<TYPE>((char*)this->Get( id ),
                                                     this->m_TypeInfo->findField( name ).offset );
  }

  template <typename TYPE>
  StompedReality::ValueDelegate<TYPE> CreateValueDelegate( StompedReality::ID id, const char* name )
  {
    return StompedReality::ValueDelegate<TYPE>(((char*)this->Get( id )) + this->m_TypeInfo->findField( name ).offset );
  }

};

template <class T>
ObjectPool<T>::ObjectPool ( int capacity )
{
  this->m_TypeInfo = StompedReality::type_system::TypeSystem::GetType< T >( );
  this->Initialize ( capacity );
}

template <class T>
ObjectPool<T>::ObjectPool ( int capacity, StompedReality::type_system::TypeInfo* info )
{
  this->m_TypeInfo = info;
  this->Initialize ( capacity );
}

template <class T>
void ObjectPool<T>::Initialize ( int capacity )
{
  this->m_ObjectSize = this->m_TypeInfo->size;
  this->m_Capacity = capacity;
  this->m_FreelistEnqueue = capacity - 1;
  this->m_FreelistDequeue = 0;
  this->m_NumOfObjects = 0;

  // init indices
  this->m_Indices = new char[this->m_Capacity * sizeof ( INDEX ) ];
  for ( unsigned i = 0; i < this->m_Capacity; ++i )
  {
    INDEX* idx  = new ( this->m_Indices + i * sizeof ( INDEX ) )INDEX ( i );
  }

  // init data;
  this->m_Data = new char[this->m_Capacity  * ( this->m_ObjectSize ) ];
  for ( unsigned i = 0; i < this->m_Capacity; ++i )
  {
    this->m_TypeInfo->constructor ( this->m_Data + i * this->m_ObjectSize );
  }
}

template <class T>
ObjectPool<T>::~ObjectPool( )
{
  for ( int i = 0; i < this->m_FreelistDequeue; ++i )
  {
    this->GetDataObject ( i )->OnDestruction( );
  }
  delete [] this->m_Indices;
  delete [] this->m_Data;
}

template <class T>
StompedReality::TYPE_ID ObjectPool<T>::GetTypeID( )
{
  return this->m_TypeInfo->typeID;
}

template <class T>
ID ObjectPool<T>::Create ( StompedReality::IGame* game )
{
  assert( this->m_NumOfObjects < this->m_Capacity );

  // get current index
  INDEX* index = this->GetIndex ( this->m_FreelistDequeue );

  // get next index
  this->m_FreelistDequeue = index->Next;

  // update id & index
  index->id += NEW_OBJECT_ID_ADD;
  index->index = this->m_NumOfObjects++;

  // update object id & call onCreation
  T* obj = this->GetDataObject ( index->index );
  obj->SetGame ( game );
  obj->OnCreation( );
  obj->SetID ( index->id );
  obj->SetType ( this->m_TypeInfo->typeID );

  // return id
  return index->id;
}

template <class T>
bool ObjectPool<T>::Contains ( ID id )
{
  INDEX* in = this->GetIndex ( id & INDEX_MASK );
  return in->id == id && in->index != USHORT_MASK;
}

template <class T>
T* ObjectPool<T>::Get ( ID id )
{
  if ( !this->Contains ( id ) )
    return 0;
  return this->GetDataObject ( this->GetIndex ( id & INDEX_MASK )->index );
}

template <class T>
void ObjectPool<T>::Remove ( ID id )
{
  INDEX* in = this->GetIndex ( id & INDEX_MASK );
  T* t = this->GetDataObject ( in->index );
  *t = *( this->GetDataObject ( --( this->m_NumOfObjects ) ) );
  ( this->GetIndex ( t->GetID( ) & INDEX_MASK ) )->index = in->index;
  in->index = 0xffff;
  ( this->GetIndex ( m_FreelistEnqueue ) )->Next  = id & INDEX_MASK;
  m_FreelistEnqueue = id & INDEX_MASK;
}

}
}
#endif // OBJECTPOOL_H
